<template>

</template>

<script>

  import { jsPlumb } from 'jsplumb'
  import {
    startCommon,
    endCommon
  } from '../flowData'
  import { _deepClone, guid } from '@/utils/index'

  let _this

  export default {
    name: 'processConfig',
    data() {
      return {
        isDrag: true,
      }
    },
    methods: {

      // ----------------- 流程配置方法，也可在页面内部复写方法 -----------------
      /**
       * 初始化数据
       */
      initData() {

      },
      /**
       * 点击节点打开配置项
       * @param config
       */
      setOption(config) {

      },
      /**
       * 鼠标拖拽事件
       * @param item
       * @param index
       */
      mousedown(item, index) {
        if(this.isDrag) {
          let body = document.getElementById('app')
          this.currentName = item.label
          // let common = {
          //   ...item,
          // }
          this.moveFlowDom = this.creatMoveDom(item)
          document.onmousemove = event => {
            body.append(this.moveFlowDom)
            let x = event.pageX
            let y = event.pageY
            if (this.moveFlowDom) {
              this.moveFlowDom.style.left = x + 'px'
              this.moveFlowDom.style.top = y + 'px'
            }
          }

          document.onmouseup = event => {
            if (this.moveFlowDom) {
              body.removeChild(this.moveFlowDom)
              this.moveFlowDom = null
            }
            this.clearEvent()
          }

          this.moveFlowDom.onmouseup = event => {
            let { pageX, pageY } = event,
              { top, left, width, height } = this.$refs.itemContainer.getBoundingClientRect()
            // 判断鼠标落点是否在指定区域内
            if (pageX - left < width && pageX > left && pageY > top && pageY - top < height) {
              let anchors = JSON.stringify(item.anchors)
              // 判断是否存在同类型节点(主要是输入节点)
              if (this.nodeData.find(f => JSON.stringify(f.anchors) === anchors && item.anchors.length === 1)) {
                _this.$message({
                  type: 'warning',
                  message: '不允许添加同类型节点!'
                })
              } else {
                let nodeDataConfig = {
                  ...item,
                  id: guid(),
                  position: {
                    left: pageX - left - 40,
                    top: pageY - top - 20
                  }
                }
                this.nodeData.push(nodeDataConfig)
                this.addFlowItem(nodeDataConfig)
              }
            }
          }
        }
      },

      /**
       * 添加锚点
       * @params dom 元素
       * @params uuid 唯一标识
       * @params position 位置 left || right
       */
      addEndpoint(dom, uuid, position) {
        this.jsPlumbObj.addEndpoint(dom, {
          anchor: _this.getAnchor(position, 1),
          uuid
        }, position === 'Right' ? startCommon : endCommon)
      },

      /**
       * 添加dom元素上锚点样式
       * @params type Left || Right
       */
      addAnchorsDom(type) {
        if (type === 'Right') {
          return `<div class="anchor-warp anchor-right"></div>`
        } else {
          return `<div class="anchor-warp anchor-left">
                           <i class="iconfont icon-bofangsanjiaoxing"></i>
                          </div>`
        }
      },

      /**
       * 创建节点dom以及修改样式
       */
      setDomStyle(config) {
        let flowDom = document.createElement('div')
        flowDom.innerHTML = this.createFlowDom(config)
        flowDom.classList.add('d-s-flow-item-container')
        flowDom.id = config.id
        flowDom.type = config.type
        flowDom.style.left = `${config.position.left}px`
        flowDom.style.top = `${config.position.top}px`

        return flowDom
      },
      /**
       * 设置流程节点是否可拖拽
       * @param flowDom
       */
      setFlowItemDrag(flowDom) {
        if(!this.isDrag) return
        this.jsPlumbObj.draggable(flowDom, {
          containment: "parent",//设定拖拽区域为父dom区域
          grid: [1, 1]
        })
      },
      /**
       * 添加流程节点
       * @param config
       */
      addFlowItem(config) {
        let flowDom = this.setDomStyle(config)
        this.itemContainer.append(flowDom)

        this.setFlowItemDrag(flowDom)

        // 根据节点端点添加左/右锚点
        config.anchors.forEach(c => {
          let uuid = `${config.id}_${c}`
          _this.addEndpoint(flowDom, uuid, c)

          let anchorStr = _this.addAnchorsDom(c)
          if(anchorStr) {
            flowDom.firstElementChild.insertAdjacentHTML('afterend', anchorStr)
          }
        })

        let mouseDownPageX, mouseDownPageY

        flowDom.addEventListener('contextmenu', e => {
          this.selectFlowData = _deepClone(config)
          this.selectFlowData.select = true
          this.changeFlowDom(this.selectFlowData)
          this.contextmenuType = true
          this.showContextmenu(e)
          e.preventDefault()
        })

        // 流程节点鼠标按下事件
        flowDom.onmousedown = (e) => {

          mouseDownPageX = e.pageX
          mouseDownPageY = e.pageY
          this.selectFlowData = config
          // 鼠标松开事件
          flowDom.onmouseup = (e) => {
            e.preventDefault()
            // 判断鼠标松开时是否和按下时位置一致 e.button 0为左击 1滚轴 2 右击
            if (e.button !== 2) {
              if (e.pageX === mouseDownPageX && e.pageY === mouseDownPageY) {
                let index = _this.nodeData.findIndex(item => item.id === config.id)
                _this.selectFlowData = index > -1 ? _this.nodeData[index] : config
                _this.setOption(_this.selectFlowData)

              } else {
                let { top, left } = this.$refs.itemContainer.getBoundingClientRect(),
                  dom = document.getElementById(config.id).getBoundingClientRect(),
                  nodeData = this.getNodeDataById(this.nodeData, config.id)
                nodeData.position.left = dom.left - left
                nodeData.position.top = dom.top - top
              }
            }
          }
        }
      },
      /**
       * 添加连线
       * @param info
       */
      addLine(info) {
        this.jsPlumbObj.connect({
          uuids: info,
          connector: ['Flowchart', { gap: 100 }],
          detachable:false, //连接线是否可以改变
        })
      },
      /**
       * 删除连线
       * @param info
       */
      deleteLine(info) {
        if(!this.isDrag) return
        this.$GlobalDeleteConfirm('是否确认删除连接线').then(() => {
          this.jsPlumbObj.deleteConnection(info)
          let sourceNode = _this.getNodeDataById(_this.nodeData, info.sourceId)
          let targetNode = _this.getNodeDataById(_this.nodeData, info.targetId)
          if(sourceNode.hasOwnProperty('isConnect')) sourceNode.isConnect = false
          if(targetNode.hasOwnProperty('isConnect')) targetNode.isConnect = false
          this.$GlobalSuccessMessage('删除成功')
        }).catch(()=>{});
      },
      /**
       * 创建节点node
       * @param nodeData
       */
      createFlowDom(nodeData) {
        return nodeData ? `<div class="flow-item-warp" data-type="${nodeData.type}">
                    <div class="flow-item-title">
                        <svg aria-hidden="true" class="icon d-s-svg-icon">
                            <use xlink:href="#${nodeData.icon}"></use>
                        </svg>
                        ${nodeData.label}
                    </div>
                    <div class="flow-item-content" data-id="${nodeData.id}">
                        <span class="flow-item-name">${nodeData.option.name}</span>
                    </div>
                </div>` : '<div></div>'
      },
      /**
       * 获取锚点位置
       * @param anchor 左右位置
       * @param anchorsNum 连接数量
       */
      getAnchor(anchor, anchorsNum) {
        let itemHeight = 28
        let bottom = 15
        let top = 32
        let temp = [], h = top + anchorsNum * itemHeight

        switch (anchor) {
          case 'Left':
            temp = [0, 0.5, -1, 0, 0, 0, anchor]
            break
          case 'Right':
            temp = [1, 0.5, 1, 0, 0, 0, anchor]
            break
          default:
        }
        return temp
      },
      /**
       * 创建拖拽时dom元素
       * @param config
       * @returns {HTMLDivElement}
       */
      creatMoveDom(config) {
        let dom = document.createElement('div'),
          labelDom = document.createElement('span'),
          svgDom = document.createElementNS('http://www.w3.org/2000/svg', 'svg')

        svgDom.classList.add('d-s-flow-move-svg')
        svgDom.innerHTML = `<use href="#${config.icon}"></use>`
        labelDom.innerHTML = config.label
        dom.classList.add('d-s-flow-move-target')
        dom.append(svgDom)
        dom.append(labelDom)

        return dom
      },
      /**
       * 删除节点
       */
      deleteFlow() {
        let id = this.selectFlowData.id,
          lineData = this.getAllConnectLineData()
        this.hiddenContextmenu()
        let hasLine = lineData.find(f => f.left === id || f.right === id)
        if (hasLine) {
          this.$GlobalDeleteConfirm('删除当前选中元素将会删除所有关联连线，确认删除吗？').then(() => {
            this.doDeleteRelationNode()
            this.doDelete()
          }).catch(()=>{});
        } else {
          this.doDelete()
        }
      },
      /**
       * 删除操作
       */
      doDelete() {
        let dom = document.getElementById(this.selectFlowData.id),
          index = this.nodeData.findIndex(f => f.id === this.selectFlowData.id)
        if (index > -1) {
          this.jsPlumbObj.remove(dom)
          this.nodeData.splice(index, 1)
          this.$GlobalSuccessMessage('删除成功')
        }
      },
      /**
       * 删除关联节点的关联关系和连接状态
       */
      doDeleteRelationNode() {
        let relationNodeData = this.nodeData.filter(f => f.lastId === this.selectFlowData.id || f.nextId === this.selectFlowData.id)
        relationNodeData.forEach(item => {
          item.isConnect = false
          item.lastId = item.lastId === this.selectFlowData.id ? '' : item.lastId
          item.nextId = item.nextId === this.selectFlowData.id ? '' : item.nextId
        })
      },
      /**
       * 获取所有连接线数据集
       */
      getAllConnectLineData() {
        let connectLineData = this.jsPlumbObj.getAllConnections()
        return connectLineData.reduce((r, c) => {
          r.push({
            id: c.id,
            left: c.sourceId,
            right: c.targetId
          })
          return r
        }, [])
      },

      /**
       * 节点触发事件时改变对应样式
       * @param config
       */
      changeFlowDom(config) {
        let dom = document.getElementById(config.id)
        // console.log(dom)

        if (config.choose || config.select) {
          dom.firstElementChild.classList.add('flow-item-warp-select')
        } else {
          dom.firstElementChild.classList.remove('flow-item-warp-select')
        }

        if (config.isOver) {
          dom.firstElementChild.classList.add('flow-item-warp-over')
        } else {
          dom.firstElementChild.classList.remove('flow-item-warp-over')
        }
      },
      /**
       * 右击显示菜单
       * @param event
       */
      showContextmenu(event) {
        // console.log(event)
        this.contextmenuShow = true
        let { pageX, pageY } = event

        let { top, left } = this.itemContainer.getBoundingClientRect()
        // 右击菜单显示位置
        _this.contextmenuTop = pageY - 50 - top
        _this.contextmenuLeft = pageX - left
      },
      /**
       * 关闭右击菜单
       */
      hiddenContextmenu() {
        if (this.contextmenuShow) {
          this.contextmenuShow = false
        }
      },
      /**
       * 根据id获取节点数据
       * @param data
       * @param id
       * @returns {*}
       */
      getNodeDataById(data, id) {
        let index = data.findIndex(f => f.id === id)
        if (index > -1) {
          return data[index]
        }
      },
      /**
       * 鼠标松开清除事件
       */
      mouseup() {
        if(this.isDrag) {
          this.clearEvent()
        }
      },
      /**
       * 清除拖拽事件
       */
      clearEvent() {
        document.onmousemove = undefined
        document.onmouseup = undefined
      },

      // ------------------------- 流程图初始化 -------------------------
      /**
       * 流程图初始化
       */
      jsPlumbInit() {
        let container = this.itemContainer
        if (!container) return

        // jsPlumb初始化
        jsPlumb.ready(() => {
          this.jsPlumbObj = jsPlumb.getInstance({
            // 连接线的样式
            PaintStyle: { strokeWidth: 1, stroke: '#567567' },
            // 箭头的样式
            ConnectionOverlays: [
              ['Arrow', { width: 10, length: 10, location: 1, visible: true }]
            ]
          })
        })

        // 设置流程容器区域
        this.jsPlumbObj.setContainer(container)

        //鼠标移动事件
        const flowMoveFunc = e => {
          let top = container.getBoundingClientRect().top,
            left = container.getBoundingClientRect().left,
            x = e.pageX - left,
            y = e.pageY - top

          this.targetEndpoint = ''

          this.nodeData.forEach(c => {
            let width = document.getElementById(c.id).getBoundingClientRect().width,
              height = document.getElementById(c.id).getBoundingClientRect().height
            // 判断鼠标是否移入到节点中
            c.isOver = x > c.position.left && x < c.position.left + width && y > c.position.top && y < c.position.top + height
            if (c.isOver) {
              this.targetEndpoint = c.id
            }
            this.changeFlowDom(c)
          })
        }

        // 连接线开始事件
        const connectionDrag = info => {
          // console.log('连线开始拖拽事件（端点）', info)

          // 端点节点选中状态
          let sourceNode = _this.getNodeDataById(_this.nodeData, info.sourceId)
          if (sourceNode) {
            sourceNode.choose = true
            _this.changeFlowDom(sourceNode)
          }
          // 添加监听鼠标移动事件
          container.addEventListener('mousemove', flowMoveFunc, false)
        }

        // 连接线结束事件
        const connectionDragStop = info => {
          // console.log('连接线拖拽停止事件（末点）', info)

          // 端点节点恢复正常状态
          let sourceNode = _this.getNodeDataById(_this.nodeData, info.sourceId)
          if (sourceNode) {
            sourceNode.choose = false
            _this.changeFlowDom(sourceNode)
          }
          // 移除鼠标移动监听事件,必须和监听时绑定同一种方法
          container.removeEventListener('mousemove', flowMoveFunc, false)
          // 移除鼠标悬浮在节点状态
          _this.nodeData.map(v => {
            v.isOver = false
            _this.changeFlowDom(v)
          })

          // console.log(_this.targetEndpoint)

          if (_this.targetEndpoint) {
            // 连接两个节点
            let sourceInfo = `${info.sourceId}_Right`
            let targetInfo = `${_this.targetEndpoint}_Left`
            setTimeout(() => {
              _this.addLine([sourceInfo, targetInfo])
              let targetNode = _this.getNodeDataById(_this.nodeData, _this.targetEndpoint)
              sourceNode.nextId = _this.targetEndpoint
              targetNode.lastId = info.sourceId
              if(sourceNode.hasOwnProperty('isConnect')) sourceNode.isConnect = true
              if(targetNode.hasOwnProperty('isConnect')) targetNode.isConnect = true
            }, 50)
          }
        }

        // 连接前检查
        const beforeDrop = info => {
          // console.log('连接前检查', info)
          if (info.sourceId === info.targetId) {
            _this.$message({
              type: 'warning',
              message: '不允许自己连自己!'
            })
            return false
          } else {
            let sourceInfo = `${info.sourceId}_Right`
            let targetInfo = `${info.targetId}_Left`
            setTimeout(() => {
              _this.addLine([sourceInfo, targetInfo])
              let sourceNode = _this.getNodeDataById(_this.nodeData, info.sourceId)
              let targetNode = _this.getNodeDataById(_this.nodeData, info.targetId)
              if(sourceNode.hasOwnProperty('isConnect')) sourceNode.isConnect = true
              if(targetNode.hasOwnProperty('isConnect')) targetNode.isConnect = true
            }, 50)
            return true
          }
        }

        // 连接线点击事件
        const lineClick = (connection, originalEvent) => {
          _this.deleteLine(connection)
        }

        // 绑定连线拖拽开始事件
        this.jsPlumbObj.bind('connectionDrag', connectionDrag)
        // 绑定连线前检查
        this.jsPlumbObj.bind('beforeDrop', beforeDrop)
        // 绑定拖拽结束事件
        this.jsPlumbObj.bind('connectionDragStop', connectionDragStop)
        // 绑定连接线点击事件
        this.jsPlumbObj.bind('click', lineClick)
      },
    },

    mounted() {
      this.$nextTick(() => {
        _this = this
      })
      //
      this.itemContainer = this.$refs.itemContainer
      //禁止指定区域内右击默认事件
      this.itemContainer.addEventListener('contextmenu', (e) => {
        e.preventDefault()
      })
      //监听指定区域点击事件
      this.itemContainer.addEventListener('click', (e) => {
        if(this.selectFlowData && !this.dialogShow) {
          this.selectFlowData.select = false
          this.changeFlowDom(this.selectFlowData)
        }
        this.hiddenContextmenu()
      })

      this.$nextTick(() => {
        this.jsPlumbInit()
        let main = document.getElementsByClassName('js-plumb-container')[0].getBoundingClientRect()
        this.viewHeight = main.height
        this.viewWidth = main.width

        this.initData()
      })
    }
  }
</script>

<style lang="scss">
  @import "./styles";
</style>
